package inspire13.duckhuntroid;


import java.util.Vector;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;

enum GameState
{
	STATE_RUNNING;
}

public class DuckHuntroidView extends SurfaceView implements SurfaceHolder.Callback
{
	public static int CANVAS_WIDTH; 
	public static int CANVAS_HEIGHT;
	
    /** Pointer to the text view to display "Paused.." etc. */
    private TextView mStatusText;

    /** The thread that actually draws the animation */
    private DuckHuntroidThread thread;

	class DuckHuntroidThread extends Thread
	{
		private boolean mRun = false;
		private GameState mState;
	        /*
         * Member (state) fields
         */
        /** The drawable to use as the background of the animation canvas */
        private Bitmap mBackgroundImage;
        private Drawable mCrosshairSprite;
        private Drawable mGrassSprite;
        private Vector<Duck> mDucks;
        
    	private Drawable msprite0;
    	private Drawable msprite1;
    	private Drawable msprite2;
    	private Drawable msprite3;
        
        private Crosshair mCrosshair;
        private Shot mShot;
        private Grass mGrass;
        
        private long mLastTime;
        
        private float mDeltaTime;
        
        private float mShotTimer = 0.0f;
        
        private float mScaleX;
        private float mScaleY;
        private float mCanvasHeight = 1.0f;
        private float mCanvasWidth = 1.0f;
     
        /** Message handler used by thread to interact with TextView */
        private Handler mHandler;

         /** Handle to the surface manager object we interact with */
        private SurfaceHolder mSurfaceHolder;

      
        public DuckHuntroidThread(SurfaceHolder surfaceHolder, Context context, Handler handler)
        {
            mSurfaceHolder = surfaceHolder;
            mHandler = handler;
            
            CreateSprites(context);
            CreateObjects(context);
         }
        
        private void CreateObjects(Context context)
        {
        	mCrosshair = new Crosshair(context, mCrosshairSprite, 400, 600, 100, 100);
        	mShot = new Shot(msprite0, msprite1, msprite2, msprite3, 110, 690, 150, 70);
        	mGrass     = new Grass(mGrassSprite, 0, 500, 1330, 120);
        }
        	
        
        private void CreateSprites(Context context)
        {
        	  Resources res = context.getResources();

              mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.background);
              mGrassSprite     = context.getResources().getDrawable(R.drawable.grass);
              mCrosshairSprite = context.getResources().getDrawable(R.drawable.crosshair);
              msprite0 = context.getResources().getDrawable(R.drawable.shot_0);
              msprite1 = context.getResources().getDrawable(R.drawable.shot_1);
              msprite2 = context.getResources().getDrawable(R.drawable.shot_2);
              msprite3 = context.getResources().getDrawable(R.drawable.shot_3);
              
              mDucks = new Vector<Duck>();
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              mDucks.add(new Duck());
              
              for(int i = 0; i < mDucks.size(); i++)
              {
            	  mDucks.elementAt(i).LoadSprites(context);
              }
        }
           
        
        public void doStart()
        {
            synchronized (mSurfaceHolder)
            {

            }
        }

       
        public void pause() 
        {
            synchronized (mSurfaceHolder)
            {
               // if (mMode == STATE_RUNNING) setState(STATE_PAUSE);
            }
        }

        
        public synchronized void restoreState(Bundle savedState)
        {
            synchronized (mSurfaceHolder)
            {
               // setState(STATE_PAUSE);
            }
        }

        @Override
        public void run()
        {
            while (mRun)
            {
                Canvas c = null;
                try 
                {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder)
                    {
                        if (mState == GameState.STATE_RUNNING)
                        {
                        	Update();
                        	Draw(c);
                        }
                    }
                }
                finally
                {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null)
                    {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }

        /**
         * Dump game state to the provided Bundle. Typically called when the
         * Activity is being suspended.
         *
         * @return Bundle with this view's state
         */
        public Bundle saveState(Bundle map)
        {
            synchronized (mSurfaceHolder)
            {
                if (map != null)
                {
//                    map.putInt(KEY_DIFFICULTY, Integer.valueOf(mDifficulty));
//                    map.putDouble(KEY_X, Double.valueOf(mX));
//                    map.putDouble(KEY_Y, Double.valueOf(mY));
//                    map.putDouble(KEY_DX, Double.valueOf(mDX));
//                    map.putDouble(KEY_DY, Double.valueOf(mDY));
//                    map.putDouble(KEY_HEADING, Double.valueOf(mHeading));
//                    map.putInt(KEY_LANDER_WIDTH, Integer.valueOf(mLanderWidth));
//                    map.putInt(KEY_LANDER_HEIGHT, Integer
//                            .valueOf(mLanderHeight));
//                    map.putInt(KEY_GOAL_X, Integer.valueOf(mGoalX));
//                    map.putInt(KEY_GOAL_SPEED, Integer.valueOf(mGoalSpeed));
//                    map.putInt(KEY_GOAL_ANGLE, Integer.valueOf(mGoalAngle));
//                    map.putInt(KEY_GOAL_WIDTH, Integer.valueOf(mGoalWidth));
//                    map.putInt(KEY_WINS, Integer.valueOf(mWinsInARow));
//                    map.putDouble(KEY_FUEL, Double.valueOf(mFuel));
                }
            }
            return map;
        }



        /**
         * Used to signal the thread whether it should be running or not.
         * Passing true allows the thread to run; passing false will shut it
         * down if it's already running. Calling start() after this was most
         * recently called with false will result in an immediate shutdown.
         *
         * @param b true to run, false to shut down
         */
        public void setRunning(boolean b)
        {
            mRun = b;
        }

        /**
         * Sets the game mode. That is, whether we are running, paused, in the
         * failure state, in the victory state, etc.
         *
         * @see #setState(GameState, CharSequence)
         * @param mode one of the STATE_* constants
         */
        public void setState(GameState mode)
        {
            synchronized (mSurfaceHolder)
            {
                setState(mode, null);
            }
        }

        /**
         * Sets the game mode. That is, whether we are running, paused, in the
         * failure state, in the victory state, etc.
         *
         * @param mode one of the STATE_* constants
         * @param message string to add to screen or null
         */
        public void setState(GameState state, CharSequence message)
        {
            /*
             * This method optionally can cause a text message to be displayed
             * to the user when the mode changes. Since the View that actually
             * renders that text is part of the main View hierarchy and not
             * owned by this thread, we can't touch the state of that View.
             * Instead we use a Message + Handler to relay commands to the main
             * thread, which updates the user-text View.
             */
            synchronized (mSurfaceHolder)
            {
                mState = state;

                if (mState == GameState.STATE_RUNNING)
                {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("text", "");
                    b.putInt("viz", View.INVISIBLE);
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
                else
                {                   
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("text", "heyyo");
                    b.putInt("viz", View.VISIBLE);
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
        }
        
        private void SetScales()
        {
        	mScaleX = mCanvasWidth / 800.0f;
        	mScaleY = mCanvasHeight / 1280.0f;
        	
        	mCrosshair.SetScale(mScaleX, mScaleY);
        	mShot.SetScale(mScaleX, mScaleY);
        	mGrass.SetScale(mScaleX, mScaleY);
        	
        	for(int i = 0; i < mDucks.size(); i++)
            {
          	  mDucks.elementAt(i).SetScale(mScaleX, mScaleY);
            }
        }

        /* Callback invoked when the surface dimensions change. */
        public void setSurfaceSize(int width, int height)
        {
            // synchronized to make sure these all change atomically
            synchronized (mSurfaceHolder)
            {
                // don't forget to resize the background image
                mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, width, height, true);
            }
        }
        
        public void setSizeAndScale(int width, int height)
        {
        	 mCanvasWidth = (float)width;
             mCanvasHeight = (float)height;
             
             CANVAS_WIDTH  = (int)mCanvasWidth;
             CANVAS_HEIGHT = (int)mCanvasHeight;
             
             mCrosshair.SetBoundries(CANVAS_HEIGHT, CANVAS_WIDTH);
             
             SetScales();
        }

        /**
         * Resumes from a pause.
         */
        public void unpause()
        {
            // Move the real time clock up to now
            synchronized (mSurfaceHolder)
            {
                mLastTime = System.currentTimeMillis() + 100;
            }
            
            setState(GameState.STATE_RUNNING);
        }
        
        public boolean doTouch(MotionEvent event)
        {
        	synchronized (mSurfaceHolder)
            {
        		if (mShotTimer > 0 || mCrosshair.GetNumOfShots() <= 0)
        		{
        			return false;
        		}
        		
        		mShotTimer = 0.5f;
        		
        		for(int i = 0; i < mDucks.size(); i++)
                {
        			Duck tempDuck = mDucks.elementAt(i);
        			 
        			boolean result = mCrosshair.Fire(tempDuck.GetX(), tempDuck.GetY(), 
        					                          tempDuck.GetX() + tempDuck.GetWidth(), tempDuck.GetY() + tempDuck.GetHeight());
	               	if (result)
	               		mDucks.elementAt(i).Hit();
                }
            }
        	
        	return true;
        }

        /**
         * Handles a key-down event.
         *
         * @param keyCode the key that was pressed
         * @param msg the original event object
         * @return true
         */
        boolean doKeyDown(int keyCode, KeyEvent msg)
        {
            synchronized (mSurfaceHolder)
            {
                return false;
            }
        }

        /**
         * Handles a key-up event.
         *
         * @param keyCode the key that was pressed
         * @param msg the original event object
         * @return true if the key was handled and consumed, or else false
         */
        boolean doKeyUp(int keyCode, KeyEvent msg)
        {
            boolean handled = false;

            synchronized (mSurfaceHolder)
            {

            }

            return handled;
        }

        private void Draw(Canvas canvas) 
        {
        	if(canvas == null)
        		return;
        	
        	// Background
        	canvas.drawBitmap(mBackgroundImage, 0, 0, null);
        	
        	// Ducks
        	for(int i = 0; i < mDucks.size(); i++)
            {
        		mDucks.elementAt(i).Draw(canvas);
            }
        	
        	// Bullets
        	mShot.Draw(canvas, mCrosshair.GetNumOfShots());
        	
        	// Grass         	
        	mGrass.Draw(canvas);
        	
        	// Crosshair
        	mCrosshair.Draw(canvas);
        }
        
        public void UpdateCrosshair(float x, float y)
        {
            mCrosshair.SetAcceleration(x, y);
        }
        
        private void Update()
        {
        	long now = System.currentTimeMillis();
           
            if (mLastTime > now) return;

            float deltaTime = (now - mLastTime) / 1000.0f;
            
            mDeltaTime = deltaTime;
            
            for(int i = 0; i < mDucks.size(); i++)
            {
        		mDucks.elementAt(i).Update(deltaTime);
            }
            
            mCrosshair.Update(deltaTime);
            
            mShotTimer -= mDeltaTime;
            mLastTime = now;
        }
    }

	public DuckHuntroidView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		
		    // register our interest in hearing about changes to our surface
		SurfaceHolder holder = getHolder();
		holder.addCallback(this);
		
		// create thread only; it's started in surfaceCreated()
		thread = new DuckHuntroidThread(holder, context, new Handler()
		{
		    @Override
		    public void handleMessage(Message m)
		    {
		        mStatusText.setVisibility(m.getData().getInt("viz"));
		        mStatusText.setText(m.getData().getString("text"));
		    }
		});
		
		setFocusable(true); // make sure we get key events
	}
	
	public DuckHuntroidThread getThread()
	{
		return thread;
	}

	 /**
     * Standard override to get key-press events.
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg)
    {
        return thread.doKeyDown(keyCode, msg);
    }

    /**
     * Standard override for key-up. We actually care about these, so we can
     * turn off the engine or stop rotating.
     */
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent msg)
    {
        return thread.doKeyUp(keyCode, msg);
    }
    
    public boolean onTouchEvent(MotionEvent event)
    {
    	return thread.doTouch(event);
    }

    /**
     * Standard window-focus override. Notice focus lost so we can pause on
     * focus lost. e.g. user switches to take a call.
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus)
    {
        if (!hasWindowFocus) thread.pause();
    }

    /**
     * Installs a pointer to the text view used for messages.
     */
    public void setTextView(TextView textView)
    {
        mStatusText = textView;
    }

    /* Callback invoked when the surface dimensions change. */
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        thread.setSurfaceSize(width, height);
    }

    /*
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder)
    {
        // start the thread here so that we don't busy-wait in run()
        // waiting for the surface to be created
    	int width = holder.getSurfaceFrame().width();
    	int height = holder.getSurfaceFrame().height();
        thread.setRunning(true);
        thread.start();
        
        if (getResources().getConfiguration().orientation == 1)
    	{
    		thread.setSizeAndScale(width, height);	
    	}
    	else
    	{
    		thread.setSizeAndScale(height, width);
    	}
    }

    /*
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        thread.setRunning(false);
        while (retry)
        {
            try
            {
                thread.join();
                retry = false;
            } 
            catch (InterruptedException e) 
            {
            }
        }
    }
}
